
#include "ev_base.h"
#include <event2/event.h>
#include <event2/event_struct.h>
#include <event2/thread.h>

EvBase* EvBase::getInstance() {
    static EvBase ev_base_;
    return &ev_base_;
}

EvBase::EvBase() :
    run_time_(0), base_(nullptr), timer_(nullptr)
{
#ifdef _WIN32
    WSADATA wsa_data;
    if (WSAStartup(0x0202, &wsa_data)) {
        return;
    }
    evthread_use_windows_threads();
#else
    evthread_use_pthreads();
#endif
    base_ = event_base_new();
    if (!base_) {
        return;
    }
    timer_ = event_new(base_, -1, EV_PERSIST, &timer_cb, (void*)this);
    if (!timer_) {
        event_base_free(base_);
        base_ = nullptr;
        return;
    }
    struct timeval tv = {1, 0};
    event_add(timer_, &tv);
}

void EvBase::timer_cb(evutil_socket_t fd, short event, void *arg) {
    EvBase *base = static_cast<EvBase *>(arg);
    base->run_time_.fetch_add(1, std::memory_order_relaxed);
}

uint32_t EvBase::run_time()
{
    return run_time_.load();
}
bool EvBase::is_ready() const
{
    return base_ != nullptr && timer_ != nullptr;
}

int EvBase::run()
{
    if (!base_) {
        return -1;
    }
    event_base_dispatch(base_);
    return 0;
}

int EvBase::exit(uint32_t ms)
{
    if (!base_) {
        return -1;
    }
    if (ms) {
        struct timeval delay = {(int)ms / 1000, (ms % 1000) * 1000};
        event_base_loopexit(base_, &delay);
    } else {
        event_base_loopexit(base_, NULL);
    }
    return 0;
}

EvBase::~EvBase()
{
    if (timer_) {
        event_free(timer_);
        timer_ = nullptr;
    }
    if (base_) {
        event_base_free(base_);
        base_ = nullptr;
    }
#ifdef _WIN32
    WSACleanup();
#endif
}

event_base *EvBase::get_base() const
{
    return base_;
}
